/*
  Copyright (c) 2011, Mikheev Rostislav
  Copyright (c) 2011, FreshFarsh team

  Steel Engine source code is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
/**
  @author Mikheev Rostislav <hacenator@gmail.com>
  @date 13.03.2011
  @brief Battle ground map class source code
*/
#include "map.h"

namespace _STEEL_NAMESPACE {

const int Map::_magick = 0x004d4646; // Initialize map file magic number

Map::Map(const WindowManager* viewport, const Camera* camera, const GUILayer* gui)
  : _edit_mode(true), //TODO(hacenator) only for test. Set to false after Save/Loading completed
  _ground_count(0),
  _ground(NULL),
  _curved_polygons_count(0),
  _curved_polygons(NULL),
  _map_borders(0,0,0,0),
  _name(NULL),
  _author_name(NULL),
  // background
  _background_index_buffer(0),
  _background_vertex_buffer(0),
  // grid
  _grid_index_buffer(0),
  _grid_vertex_buffer(0),
  _grid_zoom(0),
  _grid_frequency(10)
{
  // Test Data Set
  /*_curved_polygons_count = 1;
  _curved_polygons = new Curves[1];
  _curved_polygons[0].curves_count = 4;
  _curved_polygons[0].points = new Curve2[4];
  _curved_polygons[0].points[0].point.x = 100; _curved_polygons[0].points[0].point.y = 100;
  _curved_polygons[0].points[0].left_ipoint.x = 100; _curved_polygons[0].points[0].left_ipoint.y = 100;
  _curved_polygons[0].points[0].right_ipoint.x = 100; _curved_polygons[0].points[0].right_ipoint.y = 50;

  _curved_polygons[0].points[1].point.x = 200; _curved_polygons[0].points[1].point.y = 100;
  _curved_polygons[0].points[1].left_ipoint.x = 200; _curved_polygons[0].points[1].left_ipoint.y = 50;
  _curved_polygons[0].points[1].right_ipoint.x = 200; _curved_polygons[0].points[1].right_ipoint.y = 100;

  _curved_polygons[0].points[2].point.x = 200; _curved_polygons[0].points[2].point.y = 200;
  _curved_polygons[0].points[2].left_ipoint.x = 200; _curved_polygons[0].points[2].left_ipoint.y = 200;
  _curved_polygons[0].points[2].right_ipoint.x = 200; _curved_polygons[0].points[2].right_ipoint.y = 200;

  _curved_polygons[0].points[3].point.x = 100; _curved_polygons[0].points[3].point.y = 200;
  _curved_polygons[0].points[3].left_ipoint.x = 100; _curved_polygons[0].points[3].left_ipoint.y = 200;
  _curved_polygons[0].points[3].right_ipoint.x = 100; _curved_polygons[0].points[3].right_ipoint.y = 200;
  // Test Data Set

  // convert
  _ground_count = 1;
  _ground = new Polygon[1];
  _ground[0].vertex_count = 0; // fill vertex count to 0

  CurvesToPoly(&_curved_polygons[0], &_ground[0]);
  PolyPrepare(&_ground[0]);

  // free curves if editor mode is disabled
  if (!_edit_mode) {
    ClearCurvedPolygons();
  }*/

  // Save
  //SaveToFile("store/maps/test1.ffm"); // Saving Test
  _camera = const_cast<Camera*>(camera);
  _gui = const_cast<GUILayer*>(gui);


  _viewport = const_cast<WindowManager*>(viewport);
  _viewport_width = _viewport->width();
  _viewport_height = _viewport->height();

  CalculateEditorGrid(_viewport_width, _viewport_height);
  LoadFromFile("store/maps/test1.ffm"); // Loading test
}

Map::~Map() {
  ClearCurvedPolygons();
  ClearGroundData();
  ClearEditorGrid();
}

//----------------------------------------//

void Map::ClearGroundData() {
  for (int i; i < _ground_count; i++) {
    glDeleteBuffers(1, &_ground[i].vbo.index_buffer);
    glDeleteBuffers(1, &_ground[i].vbo.vertex_buffer);

    delete _ground[i].points;
    //delete _ground[i];
  }

  delete [] _ground;
}

//----------------------------------------//

void Map::ClearCurvedPolygons() {
  if (_curved_polygons != NULL) {
    for (int i=0; i<_curved_polygons_count; i++) {
      delete[] _curved_polygons[i].points;
    }

    delete[] _curved_polygons;
  }

  _curved_polygons = NULL;
  _curved_polygons_count = 0;
}

//----------------------------------------//

bool Map::LoadFromFile(const char * filename) {
  IOFile* file = new IOFile(filename, "rb");
  int16_t str_length;

  if (!file->Opened()) {
    sLog->Errorf("Map::LoadFromFile(\"%s\") Error: Can't open file for binary read\n", filename);
    return false;
  }

  // Clear
  if (!_edit_mode)
    ClearCurvedPolygons();

  ClearGroundData();

  sLog->Infof("Load map from file '%s'\n", filename);

  /* HEADER */
  sLog->Infof("Read header...\n");

  // read and check magick
  int magick;
  file->Read(&magick, sizeof(magick));
  if (magick != _magick) {
    sLog->Errorf("Map::LoadFromFile(\"%s\") Error: File have wrong magick number\n", filename);
    return false;
  }

  // read and check app version
  int app_version;
  file->Read(&app_version, sizeof(app_version));
  #ifdef DEBUG
  sLog->Infof("Map app_version = %d\n", app_version);
  #endif
  if (app_version > reinterpret_cast<int>(STEEL_VERSION)) {
    sLog->Errorf("Map::LoadFromFile(\"%s\") Error: This map is for the newest version of the game engine\n", filename);
    return false;
  }

  // map_name
  file->Read(&str_length, sizeof(str_length));
  if (str_length > 0) {
    _name = new wchar_t[str_length + 1];
    file->Read(_name, str_length);
  } else {
    _name = NULL;
  }
  if (_name) {
    sLog->Infof("Map name is '%s'\n", _name);
  } else {
    sLog->Info("Map name not specefied\n");
  }

  // map_author
  file->Read(&str_length, sizeof(str_length));
  if (str_length > 0) {
    _author_name = new wchar_t[str_length + 1];
    file->Read(_author_name, str_length);
  } else {
    _author_name = NULL;
  }
  if (_author_name) {
    sLog->Infof("Map author is '%s'\n", _name);
  } else {
    sLog->Info("Map author not specefied\n");
  }

  // map_border
  file->Read(&_map_borders, sizeof(_map_borders));

  /* POLYGONS DATA */
  sLog->Info("Reading polygons data...\n");
  file->Read(&_curved_polygons_count, sizeof(_curved_polygons_count));
  sLog->Infof("Curved polygons count: %d\n", _curved_polygons_count);

  _curved_polygons = new Curves[_curved_polygons_count];

  // Init ground structures
  _ground_count = _curved_polygons_count;
  _ground = new Polygon[_ground_count];

  for (int i=0; i<_curved_polygons_count; i++) {
    file->Read(&_curved_polygons[i].curves_count, sizeof(_curved_polygons[i].curves_count));
    _curved_polygons[i].points = new Curve2[_curved_polygons[i].curves_count];

    for (int j=0; j<_curved_polygons[i].curves_count; j++) {
      // point x,y
      file->Read(&_curved_polygons[i].points[j].point.x, sizeof(_curved_polygons[i].points[j].point.x));
      file->Read(&_curved_polygons[i].points[j].point.y, sizeof(_curved_polygons[i].points[j].point.y));

      // point left lever x,y
      file->Read(&_curved_polygons[i].points[j].left_ipoint.x, sizeof(_curved_polygons[i].points[j].left_ipoint.x));
      file->Read(&_curved_polygons[i].points[j].left_ipoint.y, sizeof(_curved_polygons[i].points[j].left_ipoint.y));

      // point right lever x,y
      file->Read(&_curved_polygons[i].points[j].right_ipoint.x, sizeof(_curved_polygons[i].points[j].right_ipoint.x));
      file->Read(&_curved_polygons[i].points[j].right_ipoint.y, sizeof(_curved_polygons[i].points[j].right_ipoint.y));
    }

    _ground[i].vertex_count = 0; // fill vertex count to 0
    CurvesToPoly(&_curved_polygons[i], &_ground[i]);
    PolyPrepare(&_ground[i]);
  }

  /* ENTITIES DATA */

  sLog->Infof("Map '%s'. Loading finished!\n", filename);

  return true;
}

//----------------------------------------//

bool Map::SaveToFile(const char * filename) {
  IOFile* file = new IOFile(filename, "wb");
  int16_t str_length;

  if (!file->Opened()) {
    sLog->Errorf("Map::SaveToFile(\"%s\") Error: Can't open file for binary write", filename);
    return false;
  }

  // saving aviable only in editor mode
  if (!_edit_mode)
    return false;

  sLog->Infof("Save map to '%s'\n", filename);

  // Prepare data to save
  sLog->Infof("Calclulate map size\n");
  CalculateMapSize();

  /* HEADER */
  sLog->Infof("Write header...\n");

  file->Write(&_magick, sizeof(_magick)); // magick
  file->Write(&STEEL_VERSION, sizeof(STEEL_VERSION)); // app version

  // map_name
  if (_name != NULL) {
    str_length = wcslen(_name);
  } else {
    str_length = 0;
  }

  file->Write(&str_length, sizeof(str_length));
  if (str_length != 0) {
    file->Write(_name, str_length);
  }

  // map_author
  if (_name != NULL) {
    str_length = wcslen(_author_name);
  } else {
    str_length = 0;
  }

  file->Write(&str_length, sizeof(str_length));
  if (str_length != 0) {
    file->Write(_author_name, str_length);
  }

  // map_border
  file->Write(&_map_borders, sizeof(_map_borders));


  /* POLYGONS DATA */
  sLog->Infof("Write polygons data...\n");

  // poly_count
  file->Write(&_curved_polygons_count, sizeof(_curved_polygons_count));

  for (int i=0; i<_curved_polygons_count; i++) {
    file->Write(&_curved_polygons[i].curves_count, sizeof(_curved_polygons[i].curves_count));

    for (int j=0; j<_curved_polygons[0].curves_count; j++) {
        // point x, y
        file->Write(&_curved_polygons[i].points[j].point.x, sizeof(&_curved_polygons[i].points[j].point.x));
        file->Write(&_curved_polygons[i].points[j].point.y, sizeof(&_curved_polygons[i].points[j].point.y));

        // point left lever x, y
        file->Write(&_curved_polygons[i].points[j].left_ipoint.x, sizeof(&_curved_polygons[i].points[j].left_ipoint.x));
        file->Write(&_curved_polygons[i].points[j].left_ipoint.y, sizeof(&_curved_polygons[i].points[j].left_ipoint.y));

        // point right lever x, y
        file->Write(&_curved_polygons[i].points[j].right_ipoint.x, sizeof(&_curved_polygons[i].points[j].right_ipoint.x));
        file->Write(&_curved_polygons[i].points[j].right_ipoint.y, sizeof(&_curved_polygons[i].points[j].right_ipoint.y));
    }
  }

// save entities data
  sLog->Infof("Write entities data...\n");

  // :)

  sLog->Infof("Map '%s' Saving finished!\n", filename);

  file->Close();
  delete file;

  return true;
}

//----------------------------------------//

void Map::Draw() {
  DrawGround();
}

//----------------------------------------//

void Map::DrawGround() {
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
  glLineWidth(2);

  DrawBackground();

  if (_edit_mode)
    DrawEditorGrid();

  //_camera->Apply();

  glEnableClientState(GL_COLOR_ARRAY);
  glEnableClientState(GL_VERTEX_ARRAY);
  //glEnableClientState(GL_TEXTURE_COORD_ARRAY);

  for (int i = 0; i < _ground_count; i++) {
    //glLineWidth(2);

    // map editor mode
    // mouse is near this point
    // TODO(hacenator) Mouse can't collise with several points at one time

    //sLog->Infof("Check collision Point(%d;%d) and Mouse(%d;%d)", _ground[i].points->point);
    //if (_edit_mode && CollisionCircleWithCircle(_ground[i].points->point, 1, _edit_mouse, 2)) {
    //  glPointSize(8);
    //} else {
    //  glPointSize(4);
    //}

    glColor4f(1, 0, 0, 1);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ground[i].vbo.index_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, _ground[i].vbo.vertex_buffer);

    glVertexPointer(2, GL_FLOAT, sizeof(Vertex2D), STRUCT_OFFSET_2D_POINT);
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2D), STRUCT_OFFSET_2D_TEXTURE);
    glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex2D), STRUCT_OFFSET_2D_COLOR);

    glDrawElements(GL_LINE_STRIP, _ground[i].vertex_count + 1, GL_UNSIGNED_SHORT, NULL);
    //glDrawElements(GL_POINTS, _ground[i].vertex_count + 1, GL_UNSIGNED_SHORT, NULL);
  }

  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_VERTEX_ARRAY);
  //glDisableClientState(GL_TEXTURE_COORD_ARRAY);

  // Unbind
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  glBindBuffer(GL_ARRAY_BUFFER, 0);
}

//----------------------------------------//

void Map::CreateBackground() {
  //
  //
}

void Map::DrawBackground() {
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);

  Quad2D background;

  int camera_max = 10;
  int camera_min = -10;

  RGBColor top;
  RGBColor bottom;

  top.r = 79;
  top.g = 190;
  top.b = 218;

  bottom.r = 254;
  bottom.g = 118;
  bottom.b = 64;


  background.vertex[0].point.x = 0;
  background.vertex[0].point.y = 0;
  background.vertex[1].point.x = _viewport_width;
  background.vertex[1].point.y = 0;
  background.vertex[2].point.x = _viewport_width;
  background.vertex[2].point.y = _viewport_height;
  background.vertex[3].point.x = 0;
  background.vertex[3].point.y = _viewport_height;

  float k = (_camera->GetOffsetY() * -1) / (camera_max - camera_min);

  background.vertex[0].color.r = (254 - (254 - 79) * k) + 79;
  background.vertex[0].color.g = ((190 - 118) * k) + 118;
  background.vertex[0].color.b = (218 - (218 - 64) * k) + 64;
  background.vertex[1].color.r = background.vertex[0].color.r;
  background.vertex[1].color.g = background.vertex[0].color.g;
  background.vertex[1].color.b = background.vertex[0].color.b;
  background.vertex[2].color.r = background.vertex[0].color.r;
  background.vertex[2].color.g = background.vertex[0].color.g;
  background.vertex[2].color.b = background.vertex[0].color.b;
  background.vertex[3].color.r = background.vertex[0].color.r;
  background.vertex[3].color.g = background.vertex[0].color.g;
  background.vertex[3].color.b = background.vertex[0].color.b;
  /*background.vertex[2].color.r = 254 - ((254 - 79) * k);
  background.vertex[2].color.g = ((190 - 118) * k) + 118;
  background.vertex[2].color.b = 218 - ((218 - 64) * k);
  background.vertex[3].color.r = background.vertex[2].color.r;
  background.vertex[3].color.g = background.vertex[2].color.g;
  background.vertex[3].color.b = background.vertex[2].color.b;*/

  glVertexPointer(2, GL_FLOAT, sizeof(Vertex2D), &background.vertex[0].point.x);
  glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(Vertex2D), &background.vertex[0].color.r);

  //glColor3f(0.0f, 162.0f / 255.0f, 232.0f / 255.0f);
  glPushMatrix();
  glTranslatef(
      (_camera->GetOffsetX() * -_viewport_width / 2) - _viewport_width / 2,
      (_camera->GetOffsetY() * _viewport_height / 2) - _viewport_height / 2,
      0
    );
  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  glPopMatrix();

  glDisableClientState(GL_VERTEX_ARRAY);
  glDisableClientState(GL_COLOR_ARRAY);

  // get four corners coordinates
  // calculate y over water level
  // draw.

  // Background colors:
  // higher - 79, 190, 218
  // center - 254 255 180
  // down - 254 118 64

  _camera->Reset();
  _gui->font()->Printf(0, 60, L"Colors: k:%f, r:%d; g:%d; b:%d",
      k,
      background.vertex[0].color.r,
      background.vertex[0].color.g,
      background.vertex[0].color.b
  );
}

void Map::CalculateEditorGrid(int viewport_width, int viewport_height) {
  int grid_size_x = viewport_width / _grid_frequency;
  int grid_size_y = viewport_height / _grid_frequency;

  // count of grid cells
  _grid_size = (grid_size_x + grid_size_y) * 2;

  // setting grid color
  _grid_color.r = 255;
  _grid_color.g = 255;
  _grid_color.b = 255;
  _grid_color.a = 64;

  ClearEditorGrid();

  // allocate memory for grid structures
  Point2f* grid;
  grid = new Point2f[_grid_size];

  // create vertical lines
  for (int i = 0; i < grid_size_x * 2; i+=2) {
    grid[i].x = (i / 2 * _grid_frequency);
    grid[i].y = 0.0f;
    grid[i+1].x = grid[i].x;
    grid[i+1].y = viewport_height;
  }

  int skip = grid_size_x * 2;
  // create horizontal lines
  for (int i = 0; i < grid_size_y * 2; i+=2) {
    grid[i + skip].x = 0.0f;
    grid[i + skip].y = (i / 2 * _grid_frequency);
    grid[i + skip + 1].x = viewport_width;
    grid[i + skip + 1].y = grid[i + skip].y;
  }

  // index array
  uint16_t* index_array;
  index_array = new uint16_t[_grid_size];

  for (int i = 0; i < _grid_size; i++) {
    index_array[i] = i;
  }

  // Index buffer
  _grid_index_buffer = sgBindBufferData(
      GL_ELEMENT_ARRAY_BUFFER,
      sizeof(uint16_t) * _grid_size,
      index_array,
      GL_STATIC_DRAW
  );

  // Vertex buffer
  _grid_vertex_buffer = sgBindBufferData(GL_ARRAY_BUFFER,
      sizeof(Point2f) * _grid_size,
      grid,
      GL_STATIC_DRAW
  );

  delete[] index_array;
  delete[] grid;
}

void Map::ClearEditorGrid() {
  // create grid vbo
  if (glIsBuffer(_grid_index_buffer))
    glDeleteBuffers(1, &_grid_index_buffer);

  if (glIsBuffer(_grid_vertex_buffer))
    glDeleteBuffers(1, &_grid_vertex_buffer);
}

void Map::DrawEditorGrid() {
  glEnableClientState(GL_VERTEX_ARRAY);

  glColor4ubv(&_grid_color.v[0]); // grid color

  glDisable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glLineWidth(1);

  // draw grid
  glPushMatrix();
  glTranslatef(
    (_camera->GetOffsetX() * -_viewport_width / 2) - _viewport_width / 2,// - (_camera->GetOffsetX() mod _grid_frequency),
    (_camera->GetOffsetY() * _viewport_height / 2) - _viewport_height / 2,
    0
  );
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _grid_index_buffer);
  glBindBuffer(GL_ARRAY_BUFFER, _grid_vertex_buffer);
  glVertexPointer(2, GL_FLOAT, sizeof(Point2f), 0);
  glDrawElements(GL_LINES, _grid_size, GL_UNSIGNED_SHORT, NULL);
  glPopMatrix();

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  // draw waterline
  if (_camera->GetOffsetY() >= -1
    && _camera->GetOffsetY() <= 1) {
    GLfloat waterline[2][0];

    waterline[0][0] = 0.0f; waterline[0][1] = 0.0f;
    waterline[1][0] = _viewport_width; waterline[1][1] = 0.0f;

    glDisable(GL_BLEND);
    glVertexPointer(3, GL_FLOAT, 0, &waterline[0]);
    glColor3f(0.0f, 162.0f / 255.0f, 232.0f / 255.0f);
    glLineWidth(3);

    glPushMatrix();
    glTranslatef(
      (_camera->GetOffsetX() * -_viewport_width / 2) - _viewport_width / 2,
      0, // - (_camera->GetOffsetY() mod _grid_frequency),
      0
    );
    glDrawArrays(GL_LINES, 0, 2);
    glPopMatrix();
  }

  glDisableClientState(GL_VERTEX_ARRAY);
}

//----------------------------------------//

int Map::CurveDivision(Point2f * points) {
  if (points == NULL)
    return 0;

  int length = std::max(Point2Distance(points[1], points[0]),
    Point2Distance(points[2], points[3]));

  if (length == 0)
    return 1;
  else
    return (length / 10) + 1;
}

//----------------------------------------//

bool Map::CurvesToPoly(Curves* curves, Polygon* polygon) {
  #define GET_NEXT_CURVE(x)  ( x == curves->curves_count - 1 ? curves->points[0] : curves->points[x + 1] )

  // curves not null and not empty
  if (curves == NULL || curves->curves_count == 0)
    return false;

  // polygon not null and empty
  if (polygon == NULL || polygon->vertex_count != 0)
    return false;

  //int divs = 14; // detalization

  Point2f * bezier = new Point2f[4];

  // count vertex
  for (int i = 0; i < curves->curves_count; i++) {
    Curve2 * c1 = &curves->points[i];
    Curve2 * c2 = &GET_NEXT_CURVE(i);

    bezier[0] = c1->point;
    bezier[1] = c1->right_ipoint;
    bezier[2] = c2->left_ipoint;
    bezier[3] = c2->point;

    polygon->vertex_count += CurveDivision(bezier);
  }

  // allocate memory
  polygon->points = new Vertex2D[polygon->vertex_count];

  int vertex = 0;

  // make polygon
  for (int i = 0; i < curves->curves_count; i++) {
    Curve2 * c1 = &curves->points[i];
    Curve2 * c2 = &GET_NEXT_CURVE(i);

    bezier[0] = c1->point;
    bezier[1] = c1->right_ipoint;
    bezier[2] = c2->left_ipoint;
    bezier[3] = c2->point;

    // optimization: check is curve or simple line
    // determine division level
    int divs = CurveDivision(bezier);

    // Is simple line or curved
    if (divs == 1) {
      // do nod need split, simply line

      // add vertex
      polygon->points[vertex].point = c1->point;

      // texture coordinate
      polygon->points[vertex].texture.x = 0;
      polygon->points[vertex].texture.y = 0;

      // color
      polygon->points[vertex].color.r = 0;
      polygon->points[vertex].color.g = 255;
      polygon->points[vertex].color.b = 0;
      polygon->points[vertex].color.a = 255;

      vertex++;
    } else {
      // bezier to poly
      for (int u=0; u <= divs - 1; u++) {
        float step = 1 - (float)u / (float)divs;

        // point coordinate
        polygon->points[vertex].point = Bernstein(step, bezier);

        // texture coordinate
        polygon->points[vertex].texture.x = 0;
        polygon->points[vertex].texture.y = 0;

        // color
        polygon->points[vertex].color.r = 0;
        polygon->points[vertex].color.g = 255;
        polygon->points[vertex].color.b = 0;
        polygon->points[vertex].color.a = 255;

        vertex++;
      }
    }
  }

  delete [] bezier;

  return true;
  #undef GET_NEXT_CURVE
}

//----------------------------------------//

bool Map::PolyPrepare(Polygon * poly) {
  if (poly == NULL || poly->vertex_count == 0)
    return false;

  poly->vbo.target = GL_POLYGON;

  // Index buffer
  int vertex_count = poly->vertex_count + 1;
  uint16_t index_array[vertex_count];

  for (int i = 0; i < poly->vertex_count; i++)
    index_array[i] = i;

  index_array[vertex_count - 1] = 0;

  glGenBuffers(1, &poly->vbo.index_buffer);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, poly->vbo.index_buffer);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * vertex_count, index_array, GL_STATIC_DRAW);

  // Vertex buffer
  glGenBuffers(1, &poly->vbo.vertex_buffer);
  glBindBuffer(GL_ARRAY_BUFFER, poly->vbo.vertex_buffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex2D) * poly->vertex_count, &poly->points[0].point.x, GL_STATIC_DRAW);
  //glVertexPointer(2, GL_INT, sizeof(jVertex2D), JSTRUCT_OFFSET_2D_POINT);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  return true;
}

void Map::OnMouseMove(int x, int y) {
  _edit_mouse.x = x;
  _edit_mouse.y = y;
}

void Map::OnMouseUp(int x, int y, int button) {
  //
}

void Map::OnMouseDown(int x, int y, int button) {
  //
}

void Map::CalculateMapSize() {
  _map_borders.bottom = std::numeric_limits<int>::max();
  _map_borders.top = std::numeric_limits<int>::min();

  _map_borders.right = std::numeric_limits<int>::max();
  _map_borders.left = std::numeric_limits<int>::min();

  for (int i = 0; i < _ground_count; i++) {
    for (int j = 0; j < _ground[i].vertex_count; j++) {
      // left border
      if (_ground[i].points[j].point.x > _map_borders.right) {
        _map_borders.right = _ground[i].points[j].point.x;
      // right border
      } else if (_ground[i].points[j].point.x < _map_borders.left) {
        _map_borders.left = _ground[i].points[j].point.x;
      }

      // bottom border
      if (_ground[i].points[j].point.y > _map_borders.bottom) {
        _map_borders.bottom = _ground[i].points[j].point.y;
      // top border
      } else if (_ground[i].points[j].point.y < _map_borders.top) {
        _map_borders.top = _ground[i].points[j].point.y;
      }

    }
  }
}

}
